package com.ssky.bean.validator.core.rule;

import com.ssky.bean.validator.core.VldConstants;
import com.ssky.bean.validator.core.exception.VldException;
import com.ssky.bean.validator.util.ExpUtil;
import com.ssky.bean.validator.util.DefinerUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * 明确验证器规则描述实体对象;基本规范检查和准备工作
 * <li/>验证inspect：规范校验（元数据验证）标签填写是否规范；field中字符描述类型转为java类描述（比如java.lang.String字符串转为classType实例对象）；
 * 检查rule依赖表达式是否正确；field 、rule唯一性检查；
 * <li/>准备prepare：将一些允许为null但是有默认值的进行设置； 将依赖表达式的隐式依赖，预处理为一条显示依赖表达式字符串；
 * field对应javabean中全局名称fullName、以及所属对象类型previousFieldClassType信息。
 */
public class ValidatorRuleDefiner implements IValidatorRuleDefiner {
    private static final Logger L = LoggerFactory.getLogger(ValidatorRuleDefiner.class);

    public List<ValidatorRule> define(List<ValidatorRule> validatorRuleList) {
        for (ValidatorRule vldRule : validatorRuleList) {
            if (CollectionUtils.isEmpty(vldRule.getFields()))
                throw new VldException("==ValidatorRule not have any field !");

            FieldAndRuleDefiner definer = new FieldAndRuleDefiner(vldRule.getId(), vldRule.getClassName());
            try {
                definer.inspectAndPrepareMethodRule(vldRule);
                definer.inspectAndPrepareFieldAndRule(null, vldRule.getFields());
            } catch (Exception e) {
                L.info("ValidatorRule Info --> " + definer.info());
                throw e;
            }

            definer.rpepareAndInspectRuleDepExp(vldRule);
        }
        return validatorRuleList;
    }

    private static class FieldAndRuleDefiner {
        /*  规则潜在依赖递归为显示依赖，最大递归安全深度，避免循环依赖，无限递归  */
        private static final int findAllDepExpMaxCount = 50;

        private final String validatorId;

        private final Class<?> validateBeanClassType;

//      private Integer fieldIdGlobal = Integer.valueOf(0);

        //  <ruleId - Rule>
        private final Map<String, Rule> ruleMap = new HashMap<>();
        // <fullName - Field>
        private final Map<String, Field> fieldMap = new HashMap<>();


        /**
         * @param id
         * @param validateClassName javabean的class
         */
        public FieldAndRuleDefiner(String id, String validateClassName) {
            this.validatorId = id;
            try {
                this.validateBeanClassType = Class.forName(validateClassName);
            } catch (ClassNotFoundException e) {
                throw new VldException(e.getMessage());
            }
        }

        private String info() {
            return "== validatorRule id=" + this.validatorId + " , classType = " + this.validateBeanClassType +
                    ", have " + this.fieldMap.keySet().size() + " \"field\" , hava " + this.ruleMap.keySet().size() + " \"rule\"!";
        }

        /**
         * methodRule处理:规范检查、准备
         * <li/>从规则文档角度描述：validator标签定制methodRule的检查和初始化
         *
         * @param validatorR
         */
        private void inspectAndPrepareMethodRule(ValidatorRule validatorR) {
            if (StringUtils.isBlank(validatorR.getId()) || StringUtils.isBlank(validatorR.getClassName()))
                throw new VldException("validator Config Error : \"id\" or \"classType\" cannot empty! of validatorClassName=" + validatorR.getClassName() + " !");
            if (validatorR.getMethodRules() == null)
                return;
            for (Rule methodRule : validatorR.getMethodRules()) {
                inspectMethodRule(methodRule);
                if (this.ruleMap.containsKey(methodRule.getId()))
                    throw new VldException("validator Config Error : ruleId=" + methodRule.getId() + " not unique ,  of  validatorClassType=" + validatorR.getClassName() + " !");
                this.ruleMap.put(methodRule.getId(), methodRule);
                methodRule.setDepScope(VldConstants.DepScope_Bean); //默认值
                methodRule.setType(VldConstants.RuleType_Method);//定制method
            }
        }

        /**
         * methodRule处理:规范检查
         * <li/>从规则文档角度描述：validator标签定制methodRule的规范检查
         *
         * @param rule
         */
        private void inspectMethodRule(Rule rule) {
            if (!DefinerUtils.hasBlank(rule.getType(), rule.getDepScope(), rule.getDepExp()))
                throw new VldException("MethodRule Config Error : \"type\" or \"depScope\" or \"depExp\" must be empty or null  -->" + rule.getId() + " !");
            if (DefinerUtils.hasBlank(rule.getId(), rule.getErrCode(), rule.getMethodBody()))
                throw new VldException("MethodRule Config Error :  \"id\" or \"methodBody\" cannot be empty! of RuleId=" + rule.getId() + " !");
//  todo fbTag允许空          if (DefinerUtils.hasBlank(rule.getFbTag()))
//                throw new VldException("MethodRule Config Error : \"fbTag\" cannot be empty! of RuleId=" + rule.getId() + " !");
        }

        /**
         * 校验对象内的属性及规则的规范检查、准备
         *
         * @param previousField
         * @param fields
         */
        private void inspectAndPrepareFieldAndRule(Field previousField, List<Field> fields) {
            for (Field field : fields) {
                inspectField(field);
                try {
                    field.setClassType(Class.forName(field.getClassName()));
                } catch (ClassNotFoundException e) {
                    throw new VldException("Field Config Error :  ClassNotFoundException \"className\": " + field.getClassName());
                }
                //暂不支持Map数据结构
                if (DefinerUtils.isMap(field.getClassType()))
                    throw new VldException("Don't support java.util.Map ! of FieldName=" + field.getName() + " !");
                //暂不支持Array数组结构
                if (DefinerUtils.isArray(field.getClassType()))
                    throw new VldException("Array classes are not supported ! of FieldName=" + field.getName() + " !");

                if (previousField == null) {//校验对象的第一层级属性。
                    field.setFullName(field.getName()); //构建出相对与校验对象的field属性名称的全路径
                    field.setPreField(null);//
                    field.setBeanClassType(validateBeanClassType);
                } else {//校验对象的非第一层级属性
                    field.setFullName(previousField.getFullName() + "." + field.getName());
                    field.setPreField(previousField);
                    field.setBeanClassType(validateBeanClassType);
                }

                if (this.fieldMap.containsKey(field.getFullName()))
                    throw new VldException("Rule Config Error : field fullName =" + field.getFullName() + " not unique ,  of  Field name=" + field.getName() + " !");
                this.fieldMap.put(field.getFullName(), field);


                if (field.getRules() != null)
                    for (Rule rule : field.getRules()) {
                        inspaceAndPrepareRule(rule, field);//rule的检查
                        if (this.ruleMap.containsKey(rule.getId()))
                            throw new VldException("Rule Config Error : ruleId=" + rule.getId() + " not unique ,  of  Field name=" + field.getName() + " !");
                        this.ruleMap.put(rule.getId(), rule);
                    }

                if (field.getFields() != null)//还有嵌套，继续递归
                    inspectAndPrepareFieldAndRule(field, field.getFields());
            }
        }


        private void inspectField(Field field) {
            if (!DefinerUtils.noneBlank(field.getName(), field.getClassName()))
                throw new VldException("Field Config Error : \"classType\" or \"name\" cannot empty! of field name:" + field.getName());
            if (DefinerUtils.hasNull(field.getFbTag()))
                throw new VldException("Field Config Error : \"fbTag\" cannot exist! of field name:" + field.getName());
//  允许fbvalue不存在        if (BuilderUtils.hasBlank(field.getFbValue()))
//                throw new VldException("Field Config Error : cannot have \"fbValue\" property of field name:" + field.getName());
        }

        private void inspaceAndPrepareRule(Rule rule, Field field) {
            if (DefinerUtils.hasBlank(rule.getId(), rule.getType()))
                throw new VldException("Rule Config Error :  \"id\" or \"type\" cannot empty! of RuleId=" + rule.getId() + " !");
//    todo  fbTag是否必输？        if (DefinerUtils.hasBlank(rule.getFbTag()))
//                throw new VldException("Rule Config Error : \"fbTag\" must be null or not empyt! of RuleId=" + rule.getId() + " !");
            if (!DefinerUtils.hasBlank(rule.getDepExp())) {
                if (rule.getDepExp().contains("!("))
                    // todo  现在是不是可以支持了，解析不出来？
                    throw new VldException("Config Error : \"depExp\" Do not support ' !(exp) ' expression syntax! of RuleId=" + rule.getId() + " !");
                StringTokenizer token = new StringTokenizer(rule.getDepExp().trim(), "!|&()", true);
                while (token.hasMoreTokens()) {
                    String str = token.nextToken().trim();
                    if (ExpUtil.isElement(str) &&
                            str.equals(rule.getId())) {
                        throw new VldException("Config Error :  can't depend on you own! of RuleId=" + rule.getId() + " !");
                    }
                }
            }

            if (DefinerUtils.isCollectiontElement(field)) {
                if (DefinerUtils.hasBlank(rule.getDepScope())) {
                    rule.setDepScope(VldConstants.DepScope_Local);
                } else if (!VldConstants.DepScope_Local.equals(rule.getDepScope())) {
                    throw new VldException("Rule config error: rule id=" + rule.getId() + ", \"depScope\" format error! either-or(" + VldConstants.DepScope_Local + "), or null is default(" + VldConstants.DepScope_Bean + ")");
                }
            } else {
                if (DefinerUtils.hasBlank(rule.getDepScope())) {
                    rule.setDepScope(VldConstants.DepScope_Bean);
                } else if (!VldConstants.DepScope_Bean.equals(rule.getDepScope())) {
                    throw new VldException("Rule config error: rule id=" + rule.getId() + ", \"depScope\" format error! either-or(" + VldConstants.DepScope_Bean + "), or null is default(" + VldConstants.DepScope_Bean + ")");
                }
            }
        }

        /**
         * 将规则的依赖及潜在依赖转换为显示依赖字符串。
         *
         * @param vldRule
         */
        public void rpepareAndInspectRuleDepExp(ValidatorRule vldRule) {
            if (vldRule.getMethodRules() != null)
                for (Rule rule : vldRule.getMethodRules()) {
                    try {
                        rule.setAllDepExp(this.findAllDependExp(rule, rule.getDepExp(), 0));
                    } catch (RuntimeException e) {
                        L.error("Config Error : ruleId=" + rule.getId(), e);
                        throw e;
                    }
                }
            this.findAndInitRuleDepExp(vldRule.getFields());
        }


        private void findAndInitRuleDepExp(List<Field> fields) {
            for (Field field : fields) {
                if (field.getRules() != null)
                    for (Rule rule : field.getRules()) {
                        try {
                            rule.setAllDepExp(findAllDependExp(rule, rule.getDepExp(), 0));
                        } catch (VldException e) {
                            L.error("Config Error : findAndInitRuleDepExp occur error , ruleId=" + rule.getId(), e);
                            throw e;
                        }
                    }
                if (field.getFields() != null)
                    findAndInitRuleDepExp(field.getFields());
            }
        }

        /**
         * @param exp
         * @param findCount 递归深度，第一次调用传0
         * @return
         */
        private String findAllDependExp(Rule rule ,String exp, int findCount) throws VldException {
            findCount++;
            if (findCount >= findAllDepExpMaxCount) {
                throw new VldException("将规则的依赖及潜在依赖转换为显示依赖字符串处理过程中，递归深度异常：达到（" + findAllDepExpMaxCount + ")次，请检查。");
            }
            if (exp == null || exp.trim().equals(""))
                return "";
            StringTokenizer token = new StringTokenizer(exp, "|&()", true);
            StringBuilder allDepExpStr = new StringBuilder();
            while (token.hasMoreTokens()) {
                String str = token.nextToken().trim();
                if (!ExpUtil.isOperateRPL(str)) {
                    String strNpl = str.trim();
                    if (strNpl.startsWith("!"))
                        strNpl = strNpl.substring(1);
                    Rule cond = this.ruleMap.get(strNpl);
                    if (cond == null)
                        throw new VldException("Config Error : The specified rule \"" + str + "\" not exist!");

                    String nDepExp = cond.getDepExp();
                    if (nDepExp == null || nDepExp.trim().equals("")) {
                        allDepExpStr.append(str);
                        continue;
                    }
                    allDepExpStr.append("(").append(findAllDependExp(cond,nDepExp, findCount)).append(")&" + str);
                    continue;
                }
                allDepExpStr.append(str);
            }
            return allDepExpStr.toString();
        }


    }

}
